/* caXML.c
 * XML helper pipes for ConArtist
 */

/*
 * Licensed to the ConArtist Development Team (CADT).
 * See the NOTICE file distributed with this work for additional
 * information regarding copyright ownership.
 * The CADT licenses this file to you under the Apache License, 
 * Version 2.0 (the "License"); you may not use this file except in
 * compliance with the License.
 * You may obtain a copy of the License at
 * 
 *     http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "config.h"

#include <apr_strings.h>

#include <libxml/tree.h>

#include <libxml/globals.h>
#include <libxml/threads.h>
#include <libxml/xinclude.h>
#include <libxml/xmlIO.h>

#include "conartist.h"
#include "caLog.h"
#include "caPipeline.h"
#include "caStore.h"
#include "caGenerator.h"

static apr_pool_t *xmlPool = NULL;

struct xmlDirData {
    apr_pool_t       *pool;
    struct caGenerator *gen;
    char *root;
    char *entry;
    char *directory;
    char *fnmatch;
    int               options;
};
static apr_hash_t *pathHash = NULL;
//static xmlExternalEntityLoader defaultEntityLoader = NULL;

#define XD_RECURSE    0x0001
#define XD_NAMEONLY   0x0002
#define XD_FULLPATH   0x0004


static apr_status_t xmlDirProcess(struct caPipeData *pd, struct caPipe *p)
{
    struct xmlDirData *xd = (struct xmlDirData *)p->privateData;
    apr_status_t rv = APR_EINVAL;
    xmlDocPtr doc = NULL;
    xmlNode *root = NULL;
    xmlChar *tmpMem;
    int memSz = 0;
    struct caDir *cad;
    struct caDirEntry ce;

//    caLog(CA_LOG_DEBUG, "xmlDirProcess()");

    doc = xmlNewDoc(BAD_CAST "1.0");
    if (!doc) {
        caLog(CA_LOG_WARN, "Unable to create a new XML document!");
        return APR_EINVAL;
    }

    root = xmlNewNode(NULL, BAD_CAST xd->root);
    xmlDocSetRootElement(doc, root);

    rv = caGeneratorOpenDir(&cad, xd->directory, xd->gen);
    if (rv != APR_SUCCESS) {
        caLog(CA_LOG_WARN, "Unable to open path '%s'", xd->directory);
        goto clean_exit;
    }
    while ((rv = caReadDirectory(&ce, cad)) == APR_SUCCESS) {
        if ((xd->options & XD_NAMEONLY))
            xmlNewChild(root, NULL, BAD_CAST xd->entry,
                        BAD_CAST ce.finfo.name);
        else if ((xd->options & XD_FULLPATH)) {
            char *fspath;
            rv = caDirEntryFilename(&fspath, &ce, cad);
            xmlNewChild(root, NULL, BAD_CAST xd->entry,
                        BAD_CAST (rv == APR_SUCCESS ? fspath : ce.finfo.fname));
        } else
            xmlNewChild(root, NULL, BAD_CAST xd->entry,
                        BAD_CAST ce.finfo.fname);

    }
    caCloseDirectory(cad);

    xmlDocDumpMemory(doc, &tmpMem, &memSz);
    if (memSz > 0) {

        pd->output = apr_palloc(pd->pool, memSz);
        if (pd->output) {
            pd->oLen = memSz;
            memcpy(pd->output, tmpMem, memSz);
            rv = APR_SUCCESS;
        }
    }

clean_exit:
    if (doc)
        xmlFreeDoc(doc);
    return rv;
}

apr_status_t xmlDirReadPrepare(struct caPipe **pp, 
                               struct pipelineInstance *pl,
                               xmlNode *node)
{
    struct caPipe *p = NULL;
    xmlNode *n;
    struct xmlDirData *xd = NULL;

    if (!PI_POOL(pl))
        return APR_ENOPOOL;

    p = apr_pcalloc(PI_POOL(pl), sizeof(*p));
    xd = apr_pcalloc(PI_POOL(pl), sizeof(*xd));
    if (!p || !xd)
        return APR_ENOMEM;

    xd->pool = pl->gen->pool;
    xd->gen = pl->gen;

    p->func = (pipeProcessFn *)&xmlDirProcess;
    p->rValue = APR_SUCCESS;

    for (n = node->children; n; n = n->next) {
        xmlChar *data = xmlNodeGetContent(n);
        if (xmlStrcmp(n->name, (xmlChar *)"RootElement") == 0)
            xd->root = apr_pstrdup(xd->pool, (char *)data);
        else if (xmlStrcmp(n->name, (xmlChar *)"EntryElement") == 0)
            xd->entry = apr_pstrdup(xd->pool, (char *)data);
        else if (xmlStrcmp(n->name, (xmlChar *)"Directory") == 0)
            xd->directory = apr_pstrdup(xd->pool, (char *)data);
        else if (xmlStrcmp(n->name, (xmlChar *)"Recurse") == 0)
            xd->options |= XD_RECURSE;
        else if (xmlStrcmp(n->name, (xmlChar *)"FullPath") == 0)
            xd->options |= XD_FULLPATH;
        else if (xmlStrcmp(n->name, (xmlChar *)"NameOnly") == 0)
            xd->options |= XD_NAMEONLY;
        else if (xmlStrcmp(n->name, (xmlChar *)"MatchEntry") == 0)
            xd->fnmatch = apr_pstrdup(xd->pool, (char *)data);


        xmlFree(data);

    }

    p->privateData = xd;    
    *pp = p;

    return APR_SUCCESS;
}

static apr_status_t caXmlInit(apr_pool_t *pool)
{
    apr_status_t rv;

    rv = apr_pool_create(&xmlPool, pool);
    if (rv != APR_SUCCESS)
        return rv;
    pathHash = apr_hash_make(xmlPool);
    if (!pathHash)
        return APR_ENOMEM;

    xmlInitMemory();

    LIBXML_TEST_VERSION

    caPipeRegister("xmldirread", xmlDirReadPrepare);

    return APR_SUCCESS;
}

static apr_status_t caXmlFini(void)
{
    xmlCleanupParser();
    return APR_SUCCESS;
}

struct caModule ConArtistModule = {
    0,                           /* verMajor */
    1,                           /* verMinor */
    "XML Pipe Module",           /* name */
    CAM_PIPE,                    /* type */
    &caXmlInit,                  /* init */
    &caXmlFini,                  /* fini */
};
